﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace FrameWorkBase
{
    public static class Deserialization
    {
        readonly static public Dictionary<string, int> FixClasic = new Dictionary<string, int>()
        {
            {"Dictionary`2",1 },{"List`1",2 },
            {"Boolean",3 },{"Boolean[]",4 },{"Byte",5 },{"Byte[]",6 },{"SByte",7 },{"SByte[]",8 },{"UInt16",9 },{"UInt16[]",10 },{"Int16",11 },{"Int16[]",12 },
            {"UInt32",13 },{"UInt32[]",14 },{"Int32",15 },{"Int32[]",16 },{"UInt64",17 },{"UInt64[]",18 },{"Int64",19 },{"Int64[]",20 },{"Single",21 },{"Single[]",22 },{"Double",23 },{"Double[]",24 },
            {"String",25 },{"String[]",26 }
        };

        public static T DeserializationData<T>(byte[] data)
        {
            int bufferIndex = 0;
            return (T)RoundDeserializationData(data, ref bufferIndex, typeof(T));
        }

        static object RoundDeserializationData(byte[] data,ref int BufferIndex, Type type) 
        {
            if (FixClasic.ContainsKey(type.Name))
            {
                object res = ConvertData(new ParamInfo(type.Name, type), data, ref BufferIndex);
                return res;
            }
            else
            {
               // MethodInfo[] methods = type.GetMethods();
                //PropertyInfo[] properties = type.GetProperties();
                FieldInfo[] fieldInfos = type.GetFields();

                object entity = Activator.CreateInstance(type);
                //methods[0].Invoke(entity, null);

                foreach (var item in fieldInfos)
                {
                    CanSerialize? de = item.GetCustomAttribute<CanSerialize>();
                    if (de != null)
                    {
                        ParamInfo pa = new ParamInfo(item.FieldType.Name, item.FieldType, item);
                        object res = ConvertData(pa, data, ref BufferIndex);
                        pa.Info.SetValue(entity, res);
                    }
                }
                return entity ;
            }
        }

        static object ConvertData(ParamInfo info, byte[] data,ref int BufferIndex)
        {
            switch (info.CurrentType)
            {
                case "Dictionary`2":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int length = GetLength(data, ref BufferIndex);
                        Type[] types = info.Type.GetGenericArguments();

                        IDictionary CurrentList = Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(types[0],types[1])) as IDictionary;
                        for (int i = 0; i < length; i++)
                        {
                            object key = RoundDeserializationData(data, ref BufferIndex, types[0]);
                            object value = RoundDeserializationData(data, ref BufferIndex, types[1]);

                            CurrentList.Add(key,value);
                        }
                        return CurrentList;
                    }
                    break;
                case "List`1":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int length = GetLength(data, ref BufferIndex);
                        Type[] types = info.Type.GetGenericArguments();
                       
                        IList CurrentList = Activator.CreateInstance(typeof(List<>).MakeGenericType(types[0])) as IList;
                        for (int i = 0; i < length; i++)
                        {
                            object entity = RoundDeserializationData(data, ref BufferIndex, types[0]);

                            CurrentList.Add(entity);
                        }
                        return CurrentList;
                    }
                case "Boolean":
                    {
                        byte[] result = GetData(data, 1, ref BufferIndex);
                        bool boo = Convert.ToBoolean(result[0]);
                        return boo;
                    }
                    break;
                case "Boolean[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int length = GetLength(data, ref BufferIndex);
                        bool[] array = new bool[length];
                        byte[] result = GetData(data, length, ref BufferIndex);
                        for (int i = 0; i < length; i++)
                        {
                            array[i] = Convert.ToBoolean(result[i]);
                        }
                        return array;
                    }
                    break;
                case "Byte":
                    {
                        byte[] result = GetData(data, 1, ref BufferIndex);
                        byte byt = result[0];
                        return byt;
                    }
                    break;
                case "Byte[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int length = GetLength(data, ref BufferIndex);
                        byte[] array = new byte[length];
                        byte[] result = GetData(data, length, ref BufferIndex);
                        return array;
                    }
                    break;
                case "SByte":
                    {
                        byte[] result = GetData(data, 1, ref BufferIndex);
                        sbyte sby = Convert.ToSByte(result[0]);
                        return sby;
                    }
                    break;
                case "SByte[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int length = GetLength(data, ref BufferIndex);
                        sbyte[] array = new sbyte[length];
                        byte[] result = GetData(data, length, ref BufferIndex);
                        for (int i = 0; i < length; i++)
                        {
                            array[i] = Convert.ToSByte(result[i]);
                        }
                        return array;
                    }
                    break;
                case "UInt16":
                    {
                        byte[] result = GetData(data, 2, ref BufferIndex);
                        ushort ush = BitConverter.ToUInt16(result);
                        return ush;
                    }
                    break;
                case "UInt16[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int UseByte = 2;
                        int length = GetLength(data, ref BufferIndex);
                        ushort[] array = new ushort[length];
                        byte[] result = GetData(data, length * UseByte, ref BufferIndex);
                        for (int i = 0; i < length; i++)
                        {
                            int index = i * UseByte;
                            array[i] = BitConverter.ToUInt16(new byte[] { result[index], result[index + 1] });
                        }
                        return array;
                    }
                    break;
                case "Int16":
                    {
                        byte[] result = GetData(data, 2, ref BufferIndex);
                        short sho = BitConverter.ToInt16(result);
                        return sho;
                    }
                    break;
                case "Int16[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int UseByte = 2;
                        int length = GetLength(data, ref BufferIndex);
                        short[] array = new short[length];
                        byte[] result = GetData(data, length * UseByte, ref BufferIndex);
                        for (int i = 0; i < length; i++)
                        {
                            int index = i * UseByte;
                            array[i] = BitConverter.ToInt16(new byte[] { result[index], result[index + 1] });
                        }
                        return array; 
                    }
                    break;
                case "UInt32":
                    {
                        byte[] result = GetData(data, 4, ref BufferIndex);
                        uint uin = BitConverter.ToUInt32(result);
                        return uin;
                    }
                    break;
                case "UInt32[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int UseByte = 4;
                        int length = GetLength(data, ref BufferIndex);
                        uint[] array = new uint[length];
                        byte[] result = GetData(data, length * UseByte, ref BufferIndex);
                        for (int i = 0; i < length; i++)
                        {
                            int index = i * UseByte;
                            array[i] = BitConverter.ToUInt32(new byte[] { result[index], result[index + 1], result[index + 2], result[index + 3] });
                        }
                        return array; 
                    }
                    break;
                case "Int32":
                    {
                        byte[] result = GetData(data, 4, ref BufferIndex);
                        int Int = BitConverter.ToInt32(result);
                        return Int;
                    }
                    break;
                case "Int32[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int UseByte = 4;
                        int length = GetLength(data, ref BufferIndex);
                        int[] array = new int[length];
                        byte[] result = GetData(data, length * UseByte, ref BufferIndex);
                        for (int i = 0; i < length; i++)
                        {
                            int index = i * UseByte;
                            array[i] = BitConverter.ToInt32(new byte[] { result[index], result[index + 1], result[index + 2], result[index + 3] });
                        }
                        return array;
                    }
                    break;
                case "UInt64":
                    {
                        byte[] result = GetData(data, 8, ref BufferIndex);
                        ulong ulon = BitConverter.ToUInt64(result);
                        return ulon;
                    }
                    break;
                case "UInt64[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int UseByte = 8;
                        int length = GetLength(data, ref BufferIndex);
                        ulong[] array = new ulong[length];
                        byte[] result = GetData(data, length * UseByte, ref BufferIndex);
                        for (int i = 0; i < length; i++)
                        {
                            int index = i * UseByte;
                            array[i] = BitConverter.ToUInt64(new byte[] { result[index], result[index + 1], result[index + 2], result[index + 3], result[index + 4], result[index + 5], result[index + 6], result[index + 7] });
                        }
                        return array;
                    }
                    break;
                case "Int64":
                    {
                        byte[] result = GetData(data, 8, ref BufferIndex);
                        long lon = BitConverter.ToInt64(result);
                        return lon;
                    }
                    break;
                case "Int64[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int UseByte = 8;
                        int length = GetLength(data, ref BufferIndex);
                        long[] array = new long[length];
                        byte[] result = GetData(data, length * UseByte, ref BufferIndex);
                        for (int i = 0; i < length; i++)
                        {
                            int index = i * UseByte;
                            array[i] = BitConverter.ToInt64(new byte[] { result[index], result[index + 1], result[index + 2], result[index + 3], result[index + 4], result[index + 5], result[index + 6], result[index + 7] });
                        }
                        return array;
                    }
                    break;
                case "Single":
                    {
                        byte[] result = GetData(data, 4, ref BufferIndex);
                        float flo = BitConverter.ToSingle(result);
                        return flo;
                    }
                    break;
                case "Single[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int UseByte = 4;
                        int length = GetLength(data, ref BufferIndex);
                        float[] array = new float[length];
                        byte[] result = GetData(data, length * UseByte, ref BufferIndex);
                        for (int i = 0; i < length; i++)
                        {
                            int index = i * UseByte;
                            array[i] = BitConverter.ToSingle(new byte[] { result[index], result[index + 1], result[index + 2], result[index + 3] });
                        }
                        return array;
                    }
                    break;
                case "Double":
                    {
                        byte[] result = GetData(data, 8, ref BufferIndex);
                        double dou = BitConverter.ToDouble(result);
                        return dou;
                    }
                    break;
                case "Double[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int UseByte = 8;
                        int length = GetLength(data, ref BufferIndex);
                        double[] array = new double[length];
                        byte[] result = GetData(data, length * UseByte, ref BufferIndex);
                        for (int i = 0; i < length; i++)
                        {
                            int index = i * UseByte;
                            array[i] = BitConverter.ToSingle(new byte[] { result[index], result[index + 1], result[index + 2], result[index + 3], result[index + 4], result[index + 5], result[index + 6], result[index + 7] });
                        }
                        return array;
                    }
                    break;
                case "String":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int length = GetLength(data, ref BufferIndex);
                        byte[] result = GetData(data, length, ref BufferIndex);
                        string str = Encoding.UTF8.GetString(result);
                        return str;
                    }
                    break;
                case "String[]":
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        int length = GetLength(data, ref BufferIndex);
                        string[] array = new string[length];
                        for (int i = 0; i < length; i++)
                        {
                            int strlength = GetLength(data, ref BufferIndex);
                            byte[] result = GetData(data, strlength, ref BufferIndex);
                            array[i] = Encoding.UTF8.GetString(result);
                        }
                        return array;
                    }
                    break;
                default:
                    {
                        if (IsNull(data, ref BufferIndex)) return null;
                        object obj = RoundDeserializationData(data, ref BufferIndex, info.Type);
                        return obj;
                    }
                    break;
            }
            
        }

        static int GetLength(byte[] data, ref int StartIndex)
        {
            byte[] lengtharray = new byte[] { data[StartIndex], data[StartIndex + 1], data[StartIndex + 2], data[StartIndex + 3] };
            StartIndex += 4;//除去字符串长度
            return BitConverter.ToInt32(lengtharray);
        }

        static byte[] GetData(byte[] data, int Length, ref int StartIndex)
        {
            byte[] result = new byte[Length];
            Array.Copy(data, StartIndex, result, 0, Length);
            StartIndex += Length;//除去字符串长度
            return result;
        }

        static readonly char[] NullChar = new char[] { '*', 'n', 'i', 'l', '*' };
        static bool IsNull(byte[] data,ref int StartIndex)
        {
            char isNull = Convert.ToChar(data[StartIndex]);
            if (isNull != NullChar[0])
                return false;
            else
            {
                if (data[StartIndex + 1] == NullChar[1] && data[StartIndex + 2] == NullChar[2] && data[StartIndex + 3] == NullChar[3] && data[StartIndex + 4] == NullChar[4])
                {
                    StartIndex += NullChar.Length;
                    return true;
                }
                else
                    return false;
            }
        }

    }







}
